﻿using System;
using System.Collections.Generic;
using System.Text;
using SdmxMl.Common;
using SdmxMl.Structure;
using SdmxMl.Excel;
using SdmxMl.Manager;

namespace SasPlugIn.Table
{
    public class DsdTable : BaseSasTable
    {
        #region Internal Members

        /// <summary> DSD to operate with </summary>
        private KeyFamilyType dsd;

        /// <summary>Helper to obtains refered element of each dimension </summary>
        private RefVarHelper refVar;

        /// <summary>List of dsd elements with or without Flag Attributes </summary>
        private List<BaseDimensionType> listVar;

        /// <summary> Mode of operation during columns generation </summary>
        private DsdSasExportMode exportMode;

        /// <summary> In memory artefacts + loader </summary>
        private SmManager sm;

        #endregion

        #region Constructor 


        /// <summary>
        /// C'tor for generation of an empty SAS table according to SDMX DSD
        /// </summary>
        /// <param name="ArtefactDsd">a SDMX DSD</param>
        /// <param name="prefix">Prefix for sas table</param>
        /// <param name="dsdMode">mode of interpretation for dsd attributes</param>
        /// <param name="smMgr">artefact container to use</param>
        public DsdTable(KeyFamilyType ArtefactDsd, SmManager smMgr, DsdSasExportMode dsdMode, string prefix)
            : base(ArtefactDsd, "", prefix)
        {
            dsd = ArtefactDsd;
            sm = smMgr == null ? ReferenceManager.GetSmContainerImage() : smMgr;

            // Create table name based on DSD id
            header.Name = DefaultTableName(dsd);

            exportMode = dsdMode;
            FillHeader();
        }

        #endregion

        public static string DefaultTableName(KeyFamilyType dsd)
        {
            return dsd.Id.StartsWith("DSD_") ? dsd.Id.Substring(4) : dsd.Id;
        }


        /// <summary>
        /// Fill Header columns according to artefact attributes
        /// </summary>
        protected override void FillHeader()
        {
            // In ObservationAssigned mode, store columns holding assignation and the associated codelist
            Dictionary<AttributeType, CodeListType> obsAssignedAttList = new Dictionary<AttributeType, CodeListType>();
            
            // Create helper
            refVar = new RefVarHelper(sm);
            // Prepare list, ignore flag speciality
            listVar = refVar.PrepareVarList(dsd, false);

            // For all items of DSD
            foreach (BaseDimensionType bdt in listVar)
            {
                // Associated codelist (direct or via concept)
                CodeListType clt = refVar.GetCodelistOfBaseDim(bdt);

                AttributeType att = bdt as AttributeType;
                // skip non observational attributes and flag attributes in certain modes
                if (att != null)
                {
                    if (att.AttachmentLevel != AttributeType.AttachmentLevelType.Observation && att.AttachmentLevel != AttributeType.AttachmentLevelType.Series)
                        continue;

                    if (exportMode == DsdSasExportMode.ObservationAssigned)
                    {
                        if (att.AttachmentMeasureList.Count > 0)
                        {
                            obsAssignedAttList.Add(att, clt);
                            continue;
                        }
                    }
                }

                // Get associated var type (direct in DSD or via concept if missing in DSD)
                TextFormatType tft = refVar.GetTextFormatTypeOfBaseDim(bdt);
                if (tft.TextType == TextTypeType.Undefined)
                {
                    if (clt != null)
                        tft = clt.GetDefaultTextFormat();
                }

                // Create DSD column
                header.Add(new HeaderColumn(bdt.Id, tft));
                if (clt != null)
                    header[bdt.Id].AttachedCodelist = new HeaderCodeList(clt);
            }

            // second path add secondary codelists
            foreach (KeyValuePair<AttributeType, CodeListType> kvp in obsAssignedAttList)
            {
                // add secondaty codelist to all column refered
                foreach (string s in kvp.Key.AttachmentMeasureList)
                {
                    // No main codelist
                    if (header[s].AttachedCodelist == null)
                        header[s].AttachedCodelist = new HeaderCodeList(kvp.Value);
                    else
                    {
                        header[s].AttachedCodelist = new HeaderCodeList(header[s].AttachedCodelist.CltMain, kvp.Value);
                    }
                }
                listVar.Remove(kvp.Key);
            }
        }


        /// <summary> No row created actually (no data inserted) </summary>
        /// <exception cref="System.ApplicationException"</exception>
        protected override List<string> PrepareRowDatas(object o)
        {
            throw new ApplicationException("DSD Table must be created without data rows");
        }

        public void GenerateAssociatedCodelistTables(StringBuilder sb)
        {
            HashSet<string> cltNames = new HashSet<string>();
        
            foreach (HeaderColumn col in header.Columns)
            {
                if (col.AttachedCodelist != null)
                {
                    // If not yet generated
                    if (! cltNames.Contains(col.AttachedCodelist.Name))
                    {
                    // Generate Codelist table
                    CodelistArtefactTable cltTable = 
                        new CodelistArtefactTable(col.AttachedCodelist.GetCodelist(),
                            null,
                            col.AttachedCodelist.Name, header.Prefix);
                    cltTable.SasCreateInstructions(sb);
                    cltTable.SasInsertInstructions(sb);

                    cltNames.Add(col.AttachedCodelist.Name);
                    }

                }
            }
        }
    }
}
